package peace.gui.controls.buttons {
import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;

import peace.gui.controls.Control;
import peace.gui.controls.SelectEvent;
import peace.gui.layout.Align;

public class PushButton extends Control {
    protected var out:DisplayObject;
    protected var over:DisplayObject;
    protected var down:DisplayObject;
    protected var hit:Sprite;
    protected var selectedSkin:DisplayObject;
    protected var icon:DisplayObject;
    protected var iconAlign:String = Align.CENTER;
    /**
     * реакция на mouseUp имеет смысл только в случае, если кнопка нажата
     */
    protected var pressed:Boolean = false;
    /**
     * боремся с проблемой неулета click при даун-скине
     */
    protected var clickEventFired:Boolean = false;

    public function getIcon():DisplayObject {
        return icon;
    }

    /**
     * @param tf
     * @param align выравнивание текстового поля с меткой в контроле
     */
    public function setIcon(d:DisplayObject, align:String = Align.CENTER):void {
        if (icon) {
            removeChild(icon);
        }

        iconAlign = align;

        if (!d) {
            icon = null;
            return;
        }

        icon = d;
        if (icon is InteractiveObject) {
            InteractiveObject(icon).mouseEnabled = false;
        }
        if (icon is TextField) {
            TextField(icon).selectable = false;
        }
        addChild(icon);

        arrangeIcon();
    }

    protected var currentVisible:DisplayObject;

    /**
     * Открываю out-скин для возможности выставлять начальное положение кнопки
     */
    public function get outSkin():DisplayObject {
        return out
    }

    override public function set selected(b:Boolean):void {
        super.selected = b;

        gotoAndPlay(_selected && selectedSkin ? selectedSkin : out);
    }

    /**
     * играется ли анимация всегда, <br>
     * или только по событиям мышки
     */
    public var skinsAnimationLoop:Boolean = false;

    /**
     * @param out скин
     * @param over скин
     * @param down скин
     * @param hit горячая область кнопки
     * @param selected выделенный скин
     * @param icon иконка, любой DisplayObject
     * @param iconAlign выравнивание иконки в кнопке
     */
    public function PushButton(out:DisplayObject = null, over:DisplayObject = null, down:DisplayObject = null, hit:Sprite = null, selected:DisplayObject = null, icon:DisplayObject = null, iconAlign:String = Align.CENTER) {
        this.out = out || createDefaultOutSkin();
        this.over = over;
        this.down = down;
        this.hit = hit;

        addChild(this.out);

        if (over)
            addChild(over);
        if (down)
            addChild(down);

        if (hit) {
            addChild(hit);
            hit.alpha = 0;
            hitArea = hit;
        }

        if (selected) {
            selectedSkin = selected;
            addChild(selected);
        }

        setIcon(icon, iconAlign);

        buttonMode = true;
        useHandCursor = true;
//		focusRect = false;

        initStations();

        addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false, 0, true);
        addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 0, true);
        addEventListener(MouseEvent.ROLL_OVER, onRollOver, false, 0, true);
        addEventListener(MouseEvent.ROLL_OUT, onRollOut, false, 0, true);
        addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
        addEventListener(Event.REMOVED_FROM_STAGE, onRemoveFromStage, false, 0, true);

        addEventListener(MouseEvent.CLICK, onClick, false, 100000, true);
    }

    private function onRollOut(e:MouseEvent):void {
        gotoAndPlay(_selected && selectedSkin ? selectedSkin : out);
        // после отлета считаем что не нажимали
        pressed = false;
    }

    private function onRollOver(e:MouseEvent):void {
        gotoAndPlay(_selected && selectedSkin ? selectedSkin : over);
    }

    private function onMouseDown(e:MouseEvent):void {
        gotoAndPlay(_selected && selectedSkin ? selectedSkin : down);

        pressed = true;
    }

    private function onMouseUp(e:MouseEvent):void {
        clickEventFired = false;

        if (!pressed) return;
        pressed = false;

        _selected = !_selected;

        var c:DisplayObject = _selected && selectedSkin ? selectedSkin : over;
        gotoAndPlay(c, c is MovieClip ? MovieClip(c).totalFrames : 0);

        // шлю по отпусканию ибо так правильно
        dispatchEvent(new SelectEvent(SelectEvent.CHANGE, this));

        // решаем проблему с click-event:
        // при наличии скинов ниже не срабатывало событие
        if (down || (selectedSkin && _selected)) {
            dispatchEvent(new MouseEvent(MouseEvent.CLICK));
            clickEventFired = true;
        }
    }

    private function onClick(e:MouseEvent):void {
        if (clickEventFired) {
            e.stopImmediatePropagation();
        }
    }

    private function onEnterFrame(e:Event):void {
        if (currentVisible is MovieClip && !skinsAnimationLoop) {
            var mc:MovieClip = MovieClip(currentVisible);
            if (mc.currentFrame == mc.totalFrames) {
                mc.stop();
            }
        }
    }

    private function onRemoveFromStage(e:Event):void {
        pressed = false;
    }

    /**
     * @param target
     * @param frame
     */
    protected function gotoAndPlay(target:DisplayObject, frame:uint = 1):void {
        if (!target) return;

        if (currentVisible is MovieClip) {
            MovieClip(currentVisible).gotoAndStop(1);
        }
        currentVisible.visible = false;

        target.visible = true;
        if (target is MovieClip) {
            MovieClip(target).gotoAndPlay(frame);
        }
        currentVisible = target;
    }

    protected function initStations():void {
        if (over)    over.visible = false;
        if (down)    down.visible = false;
        if (selectedSkin)    selectedSkin.visible = false;
        currentVisible = out;

        // скин по умолчанию не тормозим
//		if (out is MovieClip)
//		{
//			MovieClip(out).stop();
//		}
        if (over is MovieClip) {
            MovieClip(over).stop();
        }
        if (down is MovieClip) {
            MovieClip(down).stop();
        }

        if (selectedSkin is MovieClip) {
            MovieClip(selectedSkin).stop();
        }
    }

    protected function arrangeIcon():void {
        var padding:int = 3;

        icon.y = (height - icon.height) / 2;

        if (iconAlign == Align.CENTER) {
            icon.x = (width - icon.width) / 2;
        }
        else if (iconAlign == Align.RIGHT) {
            icon.x = width - icon.width - padding;
        }
        else {
            icon.x = padding;
        }
    }

    /**
     * @return метку если есть
     */
    override public function get value():* {
        return (icon && icon is TextField) ? TextField(icon).text : null;
    }

    override public function set value(v:*):void {
        if (!icon) {
            icon = createDefaultLabel();
            addChild(icon);
        }

        if (icon && icon is TextField) {
            TextField(icon).text = String(v);
//			TextField(icon).background = true;
//			TextField(icon).backgroundColor = 0xff0000;
            arrangeIcon();
        }
    }

    override public function set width(n:Number):void {
        out.width = n;
        if (over) {
            over.width = n;
        }
        if (down) {
            down.width = n;
        }
        if (selectedSkin) {
            selectedSkin.width = n;
        }
        if (icon) {
            if (icon is TextField) {
                TextField(icon).width = n;
//				TextField(icon).height = height;
            }
            arrangeIcon();
        }
    }

    override public function get width():Number {
        return (selectedSkin && _selected) ? selectedSkin.width : out.width;
    }

    override public function set height(n:Number):void {
        out.height = n;
        if (over) {
            over.height = n;
        }
        if (down) {
            down.height = n;
        }
        if (selectedSkin) {
            selectedSkin.height = n;
        }
        if (icon) {
            arrangeIcon();
        }
    }

    override public function get height():Number {
        return (selectedSkin && _selected) ? selectedSkin.height : out.height;
    }

    /**
     * Рисуем скин по умолчанию
     */
    protected function createDefaultOutSkin():DisplayObject {
        var d:Shape = new Shape();
        d.graphics.lineStyle(0, 0);
        d.graphics.beginFill(0xcccccc);
        d.graphics.drawRect(0, 0, 40, 20);
        return d;
    }

    protected function createDefaultLabel():TextField {
        var t:TextField = new TextField();
        t.defaultTextFormat = new TextFormat("arial", 12);
        t.selectable = t.mouseEnabled = false;
        t.autoSize = TextFieldAutoSize.LEFT;
        return t;
    }
}
}